package cn.lili.controller.wsrcom;

import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.ApiProperties;
import cn.lili.common.properties.DomainProperties;
import cn.lili.common.utils.IpUtils;
import cn.lili.controller.wxpay.BaseController;
import cn.lili.modules.payment.kit.plugin.wechat.WechatV3Api;
import cn.lili.modules.store.service.StoreSftService;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.json.FastJson;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Ret;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;

@SuppressWarnings("DuplicatedCode")
@Slf4j
@Controller
@RequestMapping({"/buyer/wsrcom"})
public class WsrcomCreateOrderController extends BaseController {
    private static final String GATEWAY_URL = "https://openapi.minda-group.com";
    private static final String OPEN_USER_ID = "10002379135024";
    private static final String KEY = "F3E923F4C8400F34202F71D5FCDFC580";

    @Autowired
    private SettingService settingService;
    @Autowired
    private StoreSftService storeSftService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * API域名
     */
    @Autowired
    private ApiProperties apiProperties;
    @Autowired
    private DomainProperties domainProperties;

    @RequestMapping("/{key}")
    @ResponseBody
    public Ret index(@PathVariable String key, HttpServletRequest request) throws Exception {
        String content = stringRedisTemplate.opsForValue().get("wxpay:" + key);
        if (StringUtils.isNotEmpty(content)) {
            if (BooleanUtils.isNotTrue(stringRedisTemplate.hasKey("wx:pay:paid:" + key))) {
                Ret param = FastJson.getJson().parse(content, Ret.class);
                String type = param.getStr("type");
                switch (type) {
                    case "alipay":
                        String url = doAlipay(key, param, request);
                        return Ret.ok("url", url).set("type", "h5");
                    default:
                        break;
                }
            } else {
                return Ret.fail("code", "paid");
            }

        }
        return Ret.fail("code", "404");
    }


    /**
     * 退款
     */
    @RequestMapping(path = "/refund", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Ret refund(@RequestParam String p, HttpServletRequest request) {
        try {
            p = base64Decode(StringUtils.trim(p));
            String content = decrypt(StringUtils.trim(p));
            log.info("退款内容：{}", content);
            Ret param = FastJson.getJson().parse(content, Ret.class);
            String orderId = param.getStr("orderId");
            WechatPaymentSetting setting = wechatPaymentSetting();
            JSONObject complaint = param.getAs("complaint");
            JSONArray complaintOrderInfo = complaint.getJSONArray("complaint_order_info");
            log.info("退款单数：{}", complaintOrderInfo.size());
            for (Object o : complaintOrderInfo) {
                JSONObject orderInfo = (JSONObject) o;
                int amount = orderInfo.getInteger("amount");
                Ret response = WechatV3Api.response(complaint.getString("complaint_id"), complaint.getString("complainted_mchid"));
                log.info("回复用户结果：{}", response);
                Ret updateRefundProgress = WechatV3Api.updateRefundProgress(complaint.getString("complaint_id"), "APPROVE");
                log.info("更新投诉审批结果：{}", updateRefundProgress);
                Ret refundParam = Ret.create().set("sp_appid", setting.getServiceAppId())
                        .set("sub_mchid", complaint.getString("complainted_mchid"))
                        .set("transaction_id", orderInfo.getString("transaction_id"))
                        .set("out_refund_no", orderInfo.getString("out_trade_no"))
                        .set("reason", "协商退款")
                        .set("refund_account", StringUtils.defaultIfEmpty(param.getStr("refundAccount"), "REFUND_SOURCE_SUB_MERCHANT"))
                        .set("notify_url", apiProperties.getBuyer() + "/buyer/payment/so/notify/WECHAT/refund")
                        .set("amount", Ret.create().set("currency", "CNY").set("refund", amount).set("total", amount));
                log.info("微信退款参数 {}", JSON.toJSONString(refundParam));
                Ret result = WechatV3Api.refundApply(refundParam);
                log.info("微信退款响应 {}", result);

                //退款申请成功
                if (result.isOk()) {
                    Ret completeRefund = WechatV3Api.completeRefund(complaint.getString("complaint_id"), complaint.getString("complainted_mchid"));
                    log.info("完成投诉处理：{}", completeRefund);
                    log.info("\n{}退款成功 {} ", orderId, orderInfo.getString("out_trade_no"));
                } else {
                    log.info("\n{}退款失败 {} ", orderId, orderInfo.getString("out_trade_no"));
                }
            }
            return Ret.ok();
        } catch (Exception e) {
            log.error("退款失败", e);
            return (Ret.fail("msg", e.getMessage()));
        }
    }

    /**
     * 预处理
     */
    @RequestMapping(path = "/pre", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Ret pre(@RequestParam String p, HttpServletRequest request) {
        try {
            p = base64Decode(StringUtils.trim(p));
            String content = decrypt(StringUtils.trim(p));
            Ret param = FastJson.getJson().parse(content, Ret.class);
            String type = param.getStr("type");
            log.info("预下单请求内容2：{}", content);

            String key = RandomStringUtils.randomAlphanumeric(10);

            String outTradeNo = param.getStr("");
            stringRedisTemplate.opsForValue().set("wxpay:" + key, param.set("key", key).set("outTradeNo", outTradeNo).toJson(), 600, TimeUnit.SECONDS);
            stringRedisTemplate.opsForValue().set("wxpay:" + param.getStr("orderId"), param.set("key", key).set("outTradeNo", outTradeNo).toJson(), 30, TimeUnit.DAYS);
            stringRedisTemplate.opsForValue().set("wxpay:" + param.getStr("outTradeNo"), param.getStr("orderId"), 30, TimeUnit.DAYS);

            log.info("\n生成预下单地址:{}", param.toJson());
            log.info("\n生成预下单地址成功:{}", getBaseUrl(request) + "/wx/user/order/" + key);

            Ret result = Ret.ok("url", domainProperties.getWap() + "/pages/go/index?key=" + key);
            result.set("type", "link");
            return result;
        } catch (Exception e) {
            log.error("生成预下单地址失败", e);
            return (Ret.fail("msg", e.getMessage()));
        }
    }

    /**
     * 回调
     */
    @RequestMapping(path = "/notify", produces = MediaType.APPLICATION_JSON_VALUE)
    public void doNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String sign = request.getParameter("sign");
        if (StringUtils.isEmpty(sign)) {
            render(response, "ERROR");
            return;
        }
        Map<String, String> params = new HashMap<>(5);
        params.put("open_userid", request.getParameter("open_userid"));
        params.put("out_trade_no", request.getParameter("out_trade_no"));
        params.put("system_order_id", request.getParameter("system_order_id"));
        params.put("pay_external_id", request.getParameter("pay_external_id"));
        params.put("total_fee", request.getParameter("total_fee"));
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        StringBuilder signContent = new StringBuilder();
        for (String key : keys) {
            String value = params.get(key);
            if (StringUtils.isNotEmpty(value)) {
                signContent.append(key).append("=").append(value).append("&");
            }
        }
        signContent.deleteCharAt(signContent.length() - 1).append(KEY);
        String newSign = DigestUtils.md5Hex(signContent.toString());
        log.info("签名内容：{}\n 对比签名：{} {} {}", signContent, sign, newSign, sign.equals(newSign));
        if (newSign.equals(sign)) {
            String orderId = params.get("out_trade_no");
            String transId = params.get("system_order_id");
            String notifyUrl = stringRedisTemplate.opsForValue().get("wxpay:notify:" + orderId);

            stringRedisTemplate.opsForValue().set("wx:pay:paid:" + orderId, transId, 30, TimeUnit.DAYS);
            log.info("--回调地址：{} : {}", "wxpay:notify:" + orderId, notifyUrl);
            if (StringUtils.isEmpty(notifyUrl)) {
                log.info("--未配置回调链接");
            } else {
                try {
                    Map<String, String> notifyParams = new HashMap<>(4);
                    notifyParams.put("orderId", orderId);
                    notifyParams.put("amount", params.get("total_fee"));
                    notifyParams.put("transId", transId);
                    notifyParams.put("timeEnd", "");
                    notifyParams.put("type", "order");

                    log.info("订单回调：{} 参数{}", notifyUrl, JsonKit.toJson(notifyParams));
                    String res = HttpKit.get(notifyUrl, notifyParams);
                    log.info("订单回调结果：{}", res);
                    JSONObject obj = JSONObject.parseObject(res);
                    if ("fail".equals(obj.getString("state"))) {
                        log.error("订单回调失败：{}", obj);
                        throw new RuntimeException();
                    }
                } catch (Exception e) {
                    log.error("订单回调失败", e);
                    throw new RuntimeException();
                }
            }
            render(response, "SUCCESS");
        } else {
            render(response, "ERROR");
        }
    }

    public String doAlipay(String key, Ret param, HttpServletRequest request) throws Exception {
        String orderId = param.getStr("orderId");
        String redisPayInfoKey = "wxpay:payInfo:" + orderId;
        boolean isCombine = ObjectUtils.defaultIfNull(param.getBoolean("isCombine"), false);
        String url;

        if (BooleanUtils.isNotTrue(stringRedisTemplate.hasKey(redisPayInfoKey))) {
            String outTradeNo = param.getStr("outTradeNo");
            Ret result = pay("ALIPAY", request, param);
            log.info("支付宝下单结果：{}", result.toJson());
            url = result.getStr("url");
            log.info("支付宝下单URL：{}", url);
            //stringRedisTemplate.opsForValue().set("wxpay:idref:" + orderId, outTradeNo);
            log.info("设置回调地址：{} : {}", "wxpay:notify:" + orderId, param.getStr("notifyUrl"));
            stringRedisTemplate.opsForValue().set("wxpay:notify:" + orderId, param.getStr("notifyUrl"), 86400, TimeUnit.SECONDS);
            stringRedisTemplate.opsForValue().set(redisPayInfoKey, url, 86400, TimeUnit.SECONDS);
        } else {
            url = stringRedisTemplate.opsForValue().get(redisPayInfoKey);
        }

        return url;

    }

    private WechatPaymentSetting wechatPaymentSetting() {
        try {
            Setting systemSetting = settingService.get(SettingEnum.WECHAT_PAYMENT.name());
            return JSON.parseObject(systemSetting.getSettingValue()).toJavaObject(WechatPaymentSetting.class);
        } catch (Exception e) {
            log.error("微信支付暂不支持", e);
            throw new ServiceException(ResultCode.PAY_NOT_SUPPORT);
        }
    }

    private String getRequestBody(Kv requestBody) {
        StringBuilder json = new StringBuilder();
        List<String> bodyKeys = new ArrayList<>(requestBody.keySet());
        Collections.sort(bodyKeys);
        for (String key : bodyKeys) {
            json.append("\"").append(key).append("\":\"").append(requestBody.getStr(key)).append("\",");
        }
        json.deleteCharAt(json.length() - 1);
        json.insert(0, "{").append("}");
        log.info("请求内容：{}", json.toString());
        return json.toString();
    }

    private String sign(Kv params) {
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        StringBuilder paramUrl = new StringBuilder();
        for (String key : keys) {
            paramUrl.append(key).append("=").append(params.get(key)).append("&");
        }
        paramUrl.deleteCharAt(paramUrl.length() - 1);
        String sign = DigestUtils.md5Hex(paramUrl + KEY);
        log.info("\n签名内容：{}\n签名：{}", paramUrl + KEY, sign);
        return sign;
    }

    @SneakyThrows
    private Ret pay(String channelType, HttpServletRequest request, Ret payParam) {

        //请求订单编号
        String outTradeNo = payParam.getStr("orderId");
        //准备支付参数
        Kv params = Kv.create();
        params.set("open_userid", OPEN_USER_ID);
        params.set("res_body", getRequestBody(
                Kv.create()
                        .set("sub_mch_id", payParam.getStr("subMchId"))
                        .set("channel_type", channelType)
                        .set("total_fee", payParam.getStr("preAmount"))
                        .set("pay_name", URLEncoder.encode("商品", "utf-8"))
                        .set("pay_body", URLEncoder.encode("商品", "utf-8"))
                        .set("notify_url", (apiProperties.getBuyer() + "/buyer/wsrcom/notify").replace("/", "\\/"))
                        .set("out_trade_no", outTradeNo)
                        .set("user_ip", IpUtils.getIpAddress(request))
                        .set("server_url", "h5.hnguanpei.cn")
        ));
        params.set("service", "gateway.unified.pay");
        params.set("sign_type", "MD5");

        params.set("sign", sign(params));

        String responseText = HttpKit.post(GATEWAY_URL, params, null);
        log.info("{}", responseText);
        JSONObject obj = JSON.parseObject(responseText);
        String respCode = obj.getString("rsp_code");
        String respMsg = obj.getString("rsp_msg");
        if ("0000".equals(respCode)) {
            String payUrl = obj.getJSONObject("request_array").getString("pay_url");
            return Ret.ok("url", payUrl);
        } else {
            return Ret.fail("msg", respMsg);
        }
    }
}
